package de.jmda.core.util.ramcomp;

import java.io.IOException;
import java.net.URL;
import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Map;

import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;

import org.apache.log4j.Logger;

import de.jmda.core.util.TypeUtil;

@SuppressWarnings("rawtypes")
public class RAMCompilerClassFileManager extends ForwardingJavaFileManager
{
	private final static Logger LOGGER =
      Logger.getLogger(RAMCompilerClassFileManager.class);

	private Map<String, JavaByteCodeObject> javaByteCodeObjectsByTypeName =
	    new HashMap<>();

	/**
	 * @param standardManger
	 */
	@SuppressWarnings("unchecked")
  public RAMCompilerClassFileManager(StandardJavaFileManager standardManager)
	{
		super(standardManager);
	}

	/**
	 * Returns class loader that is able to load types that are managed by this
	 * {@link RAMCompilerClassFileManager}.
	 */
	@Override
	public ClassLoader getClassLoader(Location location)
	{
		return new SecureClassLoader()
		{
			@Override
			protected Class<?> findClass(String name) throws ClassNotFoundException
			{
				JavaByteCodeObject javaByteCodeObject = 
						javaByteCodeObjectsByTypeName.get(name);

				byte[] bytes = javaByteCodeObject.getBytes();

				Package pckg = getPackage(name);
				
				if (pckg == null)
				{
					String packagename = TypeUtil.getPackageNameFromQualifiedTypeName(name);

					try
					{
						super.definePackage(
								packagename,
								"jMDA core API Specification", "version 1.1.0",
								"jMDA", "jMDA core", "version 1.1.0", "jMDA", null);
					}
					catch (IllegalArgumentException e)
					{
						LOGGER.warn(packagename + " was defined already");
					}
				}
				else
				{
					LOGGER.debug("found " + pckg.getName() + " for " + name);;
				}

				return super.defineClass(name, bytes, 0, bytes.length);
			}

//			@Override
//      protected Package getPackage(String name)
//      {
//				System.out.println(
//						"i'm the " + getClass().getName() + " trying to return a " +
//						Package.class.getName() + " object for " + name);
//				
//				Package result = super.getPackage(name);
//				
//				if (result == null)
//				{
//					result =
//							super.definePackage(
//									name, "jMDA core API Specification", "version 1.1.0",
//									"jMDA", "jMDA core", "version 1.1.0", "jMDA", null);
//				}
//
//				return result;
//      }

			@Override
			protected Package definePackage(String name, String specTitle,
			    String specVersion, String specVendor, String implTitle,
			    String implVersion, String implVendor, URL sealBase)
			    throws IllegalArgumentException
			{
				System.out.println(
						"i'm the " + getClass().getName() + " trying to define a " +
						Package.class.getName() + " object for " + name);
				return
						super.definePackage(
								name, "jMDA core API Specification", "version 1.1.0",
								"jMDA", "jMDA core", "version 1.1.0", "jMDA", sealBase);
			}
		};
	}

	/**
	 * Provides the compiler with a new {@link JavaByteCodeObject} instance for
	 * the given <code>typename</code> so that it can write the byte code into it.
	 */
	@Override
	public JavaFileObject getJavaFileForOutput(
			Location location, String typename, Kind kind, FileObject sibling)
			throws IOException
	{
		JavaByteCodeObject javaByteCodeObject =
				new JavaByteCodeObject(typename, kind);

		javaByteCodeObjectsByTypeName.put(typename, javaByteCodeObject);
		
		return javaByteCodeObject;
	}
}